/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> uls_istream.c </file>
  <brief>
    The procedures manipulating input(fd,str).
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, 2011.
  </author>
*/

#include "common.h"
#include "uls_istream.h"
#include "istr_fd.h"
#include "uls_log.h"

void
istr_context_init(istr_context_t *istr_ctx, int fd)
{
	memset(istr_ctx, 0x00, sizeof(istr_context_t));

	str_init(&istr_ctx->tag, &istr_ctx->tag_siz, 128);
	str_append(&istr_ctx->tag, &istr_ctx->tag_siz, 0, (uls_char_t *) "?", 1);
	str_putc(&istr_ctx->tag, &istr_ctx->tag_siz, 1, '\0');

	istr_ctx->lineno = 1;

	istr_ctx->lptr = istr_ctx->line = (uls_char_t *) "";
	istr_ctx->line_end = istr_ctx->line;

	istr_fd_init(&istr_ctx->fd_source, fd);

	str_append(&istr_ctx->cstr_pool, &istr_ctx->cstr_pool_siz, 0, NULL, 256);

	istr_ctx->tok_tower_siz = ULS_TOKTOWER_DFLSIZ;
	istr_ctx->tok_tower =  (uls_token_t *) uls_malloc(
		istr_ctx->tok_tower_siz* sizeof(uls_token_t));

	istr_ctx->gettok =  uls_gettok_raw;

	istr_ctx->fill_proc = default_fillproc;
}

void
istr_context_reset(istr_context_t *istr_ctx, int fd)
{
	istr_ctx->lineno = 1;

	istr_fd_reset(&istr_ctx->fd_source, fd);

	istr_ctx->line = istr_ctx->lptr = istr_ctx->line_end = (uls_char_t *) "";
	istr_ctx->i_literals = istr_ctx->n_literals = 0;
	istr_ctx->last_end_mark = NULL;
}

void
istr_context_deinit(istr_context_t *istr_ctx)
{
	istr_fd_deinit(&istr_ctx->fd_source);
	istr_ctx->fill_proc = default_fillproc;

	istr_ctx->tok_tower = uls_mfree(istr_ctx->tok_tower);

	str_free(&istr_ctx->cstr_pool, &istr_ctx->cstr_pool_siz);
	str_free(&istr_ctx->tag, &istr_ctx->tag_siz);
}

void
istr_context_destroy(istr_context_t *istr_ctx)
{
	istr_context_deinit(istr_ctx);
	uls_mfree(istr_ctx);
}

istr_context_t*
export_istr_context(uls_istream_t* istr)
{
	istr_context_t *istr_ctx;

	// transmit istr->fd_context to new malloc'd istr_ctx
	istr_ctx = uls_malloc(sizeof(istr_context_t));
	memcpy(istr_ctx, &istr->fd_context, sizeof(istr_context_t));

	istr_ctx->prev = NULL;

	return istr_ctx;
}

void
import_istr_context(istr_context_t *istr_ctx, uls_istream_t* istr)
{
	istr_context_t *istr_ctx0 = &istr->fd_context;

	istr_context_deinit(istr_ctx0);

	memcpy(istr_ctx0, istr_ctx, sizeof(istr_context_t));

	istr_ctx0->prev = NULL;

	uls_mfree(istr_ctx);
}

void
uls_istr_change_filler(uls_istream_t* istr, istr_filler_t proc)
{
	if (proc == NULL)
		proc = default_fillproc;
	istr->fd_context.fill_proc = proc;

	if (istr->fd_context.n_literals > 0) {
		err_log("Dismissing the resource of literal-strings");
		istr->fd_context.n_literals =  0;
	}

	istr->fd_context.i_literals = 0;
}

istr_quotetype_t*
find_quotetype_by_tokid(istr_quotetype_t *quotetypes, int n_quotetypes, int tok_id)
{
	istr_quotetype_t *e;
	int i;

	for (i=0; i<n_quotetypes; i++) {
		e = quotetypes + i;
		
		if (e->tok_id == tok_id) return e;
	}
	
	return NULL;
}

uls_char_t*
istr_quotetype_start_mark(uls_istream_t* istr, int tok_id)
{
	istr_quotetype_t *q;

	q = find_quotetype_by_tokid(
		istr->quotetypes, istr->n_quotetypes, tok_id);
	if (q == NULL) {
		err_log("No matching quote-string for tok-id(%d) found", tok_id);
		return NULL;
	}

	return q->start_mark;
} 

istr_quotetype_t*
istr_find_quotetype(uls_istream_t* istr, uls_char_t *ptr, int len)
{
	istr_quotetype_t *quotetypes = istr->quotetypes, *e;
	int n_quotetypes = istr->n_quotetypes;
	uls_char_t *str;
	int i;

	for (i=0; i<n_quotetypes; i++) {
		e = quotetypes + i;

		// e->len_start_mark > 0
		if (len < e->len_start_mark) continue;

		str = e->start_mark;
		if (ptr[0] == str[0]) {
			if (e->len_start_mark == 1 || !uls_strncmp(ptr+1,str+1, e->len_start_mark-1)) {
				return e;
			}
		}
	}

	return NULL;
}

// <brief>
// Delete the literal string analyzing interface starting with 'prefix'
// </brief>
// <parm name="prefix">Finds the literal string analyzer.</parm>
// <return>0 if it succeeds, otherwise -1</return>
int
istr_delete_lit_analyzer(uls_istream_t* istr, const uls_char_t* prefix)
{
	istr_quotetype_t *qmt;
	int i;

	for (i=0; ; i++) {
		if (i >= istr->n_quotetypes) {
			return 0;
		}

		qmt = istr->quotetypes + i;
		if (uls_streql(qmt->start_mark, prefix)) {
			break;
		}
	}

	if (i < istr->n_quotetypes-1) {
		memcpy(istr->quotetypes + i, istr->quotetypes + i+1,
			(istr->n_quotetypes - 1 - i) *sizeof(istr_quotetype_t));
	}

	--istr->n_quotetypes;
	return 1;
}

// <brief>
// Changes the literal string analyzer to 'proc'.
// The 'proc' will be applied to the literal string starting with 'prefix'.
// </brief>
// <parm name="istr">The input stream for ULS</parm>
// <parm name="prefix">The prefix of literal string that will be processed by 'proc'</parm>
// <parm name="lit_analyzer">A new procedure for analyzing literal strings.</parm>
// <return>0 if it succeeds, otherwise -1</return>
int
istr_change_lit_analyzer(uls_istream_t* istr,
	const uls_char_t* prefix, istr_analyzer_t lit_analyzer)
{
	istr_quotetype_t *qmt;
	int i, stat = 0;

	if (lit_analyzer == NULL) {
		lit_analyzer = default_lit_analyzer;
	}

	for (i=0; i<istr->n_quotetypes; i++) {
		qmt = istr->quotetypes + i;
		if (uls_streql(qmt->start_mark, prefix)) {
			qmt->literal_analyzer = lit_analyzer;
			stat = 1;
			break;
		}
	}

	return stat;
}

void
uls_istr_init(uls_istream_t* istr)
{
	uls_char_t *tbl = istr->ch_context;
	int  ch;

	memset(istr, 0x00, sizeof(uls_istream_t));

	for (ch=0; ch < ULS_SYNTAX_TABLE_SIZE; ch++) {
		if (isalpha(ch)) {
			tbl[ch] = ULS_CH_IDFIRST | ULS_CH_ID;
		} else if (isdigit(ch)) {
			tbl[ch] = ULS_CH_ID | ULS_CH_DIGIT;
		} else if (isgraph(ch)) {
			tbl[ch] = ULS_CH_1;
		} else { // ctrl-chars
			tbl[ch] = 0;
		}
	}

	tbl['-'] |= ULS_CH_DIGIT;
	tbl['.'] |= ULS_CH_DIGIT;

	istr_context_init(&istr->fd_context, -1);
	istr->fd_context.fd_source.flags |= ISTR_FD_EOF;

	istr->fd_context.fill_proc = default_fillproc;
}

// <brief>
// Sets the 'LineNum' forcibly or intially.
// </brief>
// <parm name="istr">The input stream for ULS</parm>
// <parm name="tagstr">The new value to be updated</parm>
// <parm name="lno">The new value to be updated</parm>
// <return>none</return>
void
uls_istr_set_tag(uls_istream_t* istr, const char* tagstr, int lno)
{
	int k;

	if (tagstr != NULL) {
		k = str_append(&istr->fd_context.tag, &istr->fd_context.tag_siz, 0, (uls_char_t *) tagstr, -1);
		str_putc(&istr->fd_context.tag, &istr->fd_context.tag_siz, k, '\0');
	}

	if (lno > 0) {
		istr->fd_context.lineno = lno;
	}
}

// <brief>
// Sets the 'LineNum' forcibly or intially.
// </brief>
// <parm name="istr">The input stream for ULS</parm>
// <parm name="delat">The difference value to be added to the current value/parm>
// <return>none</return>
void
uls_istr_inc_lineno(uls_istream_t* istr, int delta)
{
	int lno;

	lno = istr->fd_context.lineno + delta;
	uls_istr_set_tag(istr, NULL, lno);
}

void
uls_istr_set_line(uls_istream_t* istr, char* line, int len)
{
	if (line == NULL) {
		line = "";
		len = 0;
	} else if (len < 0) {
		len = strlen(line);
	}

	istr->fd_context.lptr = istr->fd_context.line = (uls_char_t *) line;
	if ((istr->fd_context.line_end = (uls_char_t *) (line+len)) < (uls_char_t *) line)
		err_panic("%s: invalid param(len=%d)!", __FUNCTION__, len);
}

void
uls_istr_reset(uls_istream_t* istr)
{
	uls_istr_set_line(istr, NULL, -1);
	str_free(&istr->fd_context.tag, &istr->fd_context.tag_siz);
	istr->fd_context.lineno = 1;
}

void
uls_istr_deinit(uls_istream_t* istr)
{
	uls_istr_reset(istr);
	istr_context_deinit(&istr->fd_context);
}

int
uls_istr_left_bytes(uls_istream_t* istr)
{
	uls_char_t* lptr = istr->fd_context.lptr;

	if (lptr==NULL) return 0;

	return (int) (istr->fd_context.line_end - lptr);
}

int
default_fillproc(uls_istream_t* istr,
	uls_char_t** p_line, uls_literal_t* lits, int* n_lits)
{
	*n_lits = 0;
	return 0; // end of input
}

extern int
__generic_lit_analyzer(int verbatim,
	istr_fd_t *fd_src, uls_char_t* quote_str, csz_str_t *ss_dst, uls_literal_t* lit, int* p_lno);

int
verbatim_lit_analyzer(istr_fd_t *fd_src, uls_char_t* quote_str, csz_str_t *ss_dst, uls_literal_t* lit, int* p_lno)
{
	return __generic_lit_analyzer(1, fd_src, quote_str, ss_dst, lit, p_lno);
}

int
default_lit_analyzer(istr_fd_t *fd_src, uls_char_t* quote_str, csz_str_t *ss_dst, uls_literal_t* lit, int* p_lno)
{
	return __generic_lit_analyzer(0, fd_src, quote_str, ss_dst, lit, p_lno);
}
